<!DOCTYPE html>
<html>
    <head>
        <META NAME="Author" CONTENT="emu">
        <META NAME="Keywords" CONTENT="webcrypto AES-CBC AES-GCM">
    </head>
    <body>
        <div id="out"></div>
        <script type="text/javascript">
            function output(sign) {
                document.getElementById("out").innerHTML += sign + "<br>";
            }
            function bufferToHex(b){
                var dataview = new DataView(b);
                result = "";
                for (var i = 0; i < b.byteLength; i += 4) {
                    tmp = dataview.getUint32(i).toString(16);
                    result += (tmp.length == 8 ? "" : "0") + tmp;
                }
                return result;
            }

            function bufferToString(b){
                //new TextDecoder().decode(b)
                var hex=bufferToHex(b);
                var result=unescape(hex.replace(/(..)/g,"%$1"));
                return result;
            }
            function stringToBuffer(s){
                //new TextEncoder().encode(s);
                 var a = s.split("");
                 for (var i = 0; i < a.length; i++) {
                        a[i] = a[i].charCodeAt(0)
                 };
                 var result = new Uint8Array(a);
                 return result;
            }


        var textToBeEncrypted = 'Hello World!';
        var bufferToBeEncrypted=stringToBuffer(textToBeEncrypted);
        var bufferToBeDecrypted;
        var pwd="let me try this password"
        var password = stringToBuffer(pwd);
        var sAlg="AES-CBC";//AES-GCM部分浏览器不支持
        var c = window.crypto || window.msCrypto;
        var subtle = c.subtle || c.webkitSubtle;
        var iv = c.getRandomValues(new Uint8Array(16));
        var alg = { name: sAlg, iv: iv };
        var op=c.subtle.digest('SHA-256', password)
        var encryptKey,decryptKey,hash;
        if(("then" in op)){
            op.then(function(buffer){
                hash=buffer;
                c.subtle.importKey('raw', hash, alg, false, ['encrypt']).then(function(buffer){
                    encryptKey=buffer;
                    c.subtle.importKey('raw', hash, alg, false, ['decrypt']).then(function(buffer){
                    decryptKey=buffer;
                    doEncrypt(alg,encryptKey);
                    })
                })
            });
        }else{
            op.oncomplete=function(e){
                var hash=e.target.result;
                var op=c.subtle.importKey('raw', hash, alg, false, ['encrypt']);
                op.oncomplete=function(e){
                    encryptKey=e.target.result;
                    op=c.subtle.importKey('raw', hash, alg, false, ['decrypt']);
                    op.oncomplete=function(e){
                        decryptKey=e.target.result;
                        encryptWithCryptoOperation(alg,encryptKey);
                    }
                }
            }
        }
        function doEncrypt(alg,encryptKey){
            var op = c.subtle.encrypt(alg, encryptKey, bufferToBeEncrypted);
            op.then(function(buffer){
                    bufferToBeDecrypted=buffer;
                    output("pwd: <b><i>"+pwd+"</i></b> alg:<b>"+sAlg+"</b> <br>encrypt("+textToBeEncrypted + ")="+ bufferToHex(buffer));
                    doDecrypt(alg,decryptKey);
                })
        }

        function doDecrypt(alg,decryptKey){
            var op = c.subtle.decrypt(alg, decryptKey, bufferToBeDecrypted);
            op.then(function(buffer){
                output("pwd:<b><i>"+pwd+"</i></b>  alg:<b>"+sAlg+"</b> <br>decrypt("+bufferToHex(bufferToBeDecrypted) + ")="+ bufferToString(buffer));
            })
        }

        function encryptWithCryptoOperation(alg,encryptKey){
            var op = c.subtle.encrypt(alg, encryptKey);
            op.onerror=function(e){output(sAlg+"  unsupported")}
            op.process(bufferToBeEncrypted);
            op.oncomplete=function(e){
                bufferToBeDecrypted=e.target.result
                    output("pwd: <b><i>"+pwd+"</i></b> alg:<b>"+sAlg+"</b> <br>encrypt("+textToBeEncrypted + ")="+ bufferToHex(bufferToBeDecrypted));
                //decryptWithCryptoOperation(alg,decryptKey)
                setTimeout(decryptWithCryptoOperation,0,alg,decryptKey)
            }
            op.finish();
        }        
        function decryptWithCryptoOperation  (alg,decryptKey){
            var op = c.subtle.decrypt(alg, decryptKey);
            op.onerror=function(e){(sAlg+"  unsupported")}
            op.process(bufferToBeDecrypted);
            op.oncomplete=function(e){
                if(!e.target.result){
                    output("IE is crazy!<br>");
                    encryptWithCryptoOperation(alg,encryptKey)
                }else
                    output("pwd:<b><i>"+pwd+"</i></b>  alg:<b>"+sAlg+"</b> <br>decrypt("+bufferToHex(bufferToBeDecrypted) + ")="+ bufferToString(e.target.result));
            }
            op.finish();
        }
        </script>
    </body>
</html>